/*******************************************************************************
*  Copyright (C) 2016  SaschArt                                                *
*  This program is free software: you can redistribute it and/or modify        *
*  it under the terms of the GNU General Public License as published by        *
*  the Free Software Foundation, either version 3 of the License, or           *
*  (at your option) any later version.                                         *
*                                                                              *
*  GNU General Public License (http://www.gnu.org/licenses/)for more details.  *
*******************************************************************************/

desc: SLimiter for easy mastering. Output audio limiting with input gain correction. Show graphic peaks and attenuation.

slider1:0<-12,12,0.1>Input gain
slider2:40<1,100,1>Attack (ns)
slider3:100<1,1500,1>Release (ms)
slider4:70<0,100,1>Link Stereo (%)
slider5:0<-10,0,0.1>Limit out
slider6:5<0,10,1>-recommended attenuation


@init

sc=6/log(2);
HOLDTIME = srate/128;

r1Timer = 0;
r2Timer = HOLDTIME/2;

r1TimerO = 0;
r2TimerO = HOLDTIME/2;

g_meter=0;
g_meterL=g_meterR=max_meterL=max_meterR=max_LR=0;
posL=posR=posLR=0;

max_x=300;                     // must be round
refresh=50;                    // refresh graph (ms)
stime=refresh*srate/1000;
arr_in = 0;
arr_out = arr_in + max_x;
height_db=20;
gr_inL=gr_inR=gr_outL=gr_outR=-999;
base_db=min_show=-22;
max_show=10;
gr_hide="attenuation";

ext_noinit=1;

@slider
thresh = 10^(-slider1/20);
ceiling = 10^(slider5/20);
volume = ceiling/thresh;
attack = exp(-3/(srate*max(slider2/1000000,0.000001)));
release = exp(-3/(srate*max(slider3/1000,0.001)));
link = sqrt(slider4*0.01);

@sample
play_state==1 ? (

gr_hide=="attenuation" ? (
  gr_inL=max(gr_inL,abs(spl0*volume));
  gr_inR=max(gr_inR,abs(spl1*volume));
);

maxSpls=abs(spl0);

(r1Timer+=1) > HOLDTIME ? (r1Timer = 0; max1Block = 0; );
max1Block = max(max1Block,maxSpls);
(r2Timer+=1) > HOLDTIME ? (r2Timer = 0; max2Block = 0; );
max2Block = max(max2Block,maxSpls);

envT = max(max1Block,max2Block);

maxSplsO=abs(spl1);

(r1TimerO+=1) > HOLDTIME ? (r1TimerO = 0; max1BlockO = 0; );
max1BlockO = max(max1BlockO,maxSplsO);
(r2TimerO+=1) > HOLDTIME ? (r2TimerO = 0; max2BlockO = 0; );
max2BlockO = max(max2BlockO,maxSplsO);

envTO = max(max1BlockO,max2BlockO);

env = max(env,envO*link);
envO = max(env*link,envO);

env = env < envT ? envT + attack*(env-envT) : envT + release*(env-envT);
(env > thresh) ? gainL = (g_meterL=(thresh / env))*volume : (g_meterL=1; gainL=volume;);

envO = envO < envTO ? envTO + attack*(envO-envTO) : envTO + release*(envO-envTO);
(envO > thresh) ? gainR = (g_meterR=(thresh / envO))*volume : (g_meterR=1; gainR=volume;);

spl0*=gainL;
spl1*=gainR;
g_meterL=log10(g_meterL)*-20;
g_meterR=log10(g_meterR)*-20;
g_meterL>max_meterL ? (max_meterL=g_meterL; posL=play_position);
g_meterR>max_meterR ? (max_meterR=g_meterR; posR=play_position);
abs(g_meterL-g_meterR)>abs(max_LR) ? (max_LR=g_meterL-g_meterR; posLR=play_position);

gr_outL=max(gr_outL,abs(spl0));
gr_outR=max(gr_outR,abs(spl1));
nr>stime ? (
  gr_hide=="attenuation" ? (vol=max((log(gr_inL)*sc),(log(gr_inR)*sc)))
  : (vol=max(g_meterL,g_meterR));
  vol>height_db+base_db ? (
    base_db=min(ceil(vol-height_db),max_show-height_db);
    desc_release=64;
  );
  arr_in[max_x]=vol;
  arr_in+=1;
  vol=max((log(gr_outL)*sc),(log(gr_outR)*sc));
  vol < base_db ? (
    desc_release-=1;
    desc_release<0 ? (
      base_db=max(base_db-1,min_show);
      desc_release=16;
    );
    
  );
  arr_out[max_x]=vol;
  arr_out+=1;
  nr=0;
  gr_outL=gr_outR=gr_inL=gr_inR=-999;
);
nr+=1;

);

@gfx 588 80
gfx_r=gfx_g=gfx_b=1;

gfx_x=125; gfx_y=8; gfx_a=0.8;
gfx_drawstr("Attenuation:");

max_meterL>slider6-1 ? (gfx_r=gfx_b=0);
max_meterL>slider6 ? (gfx_r=1);
max_meterL>slider6+1 ? (gfx_g=0);
gfx_a=1; gfx_x+=5;
mouse_x < 325 && mouse_x > 220 && mouse_y < 26 && mouse_y >0  ? (
  gfx_a=0.8;
  mouse_cap ? (max_meterL=max_meterR=posL=posR=0; gfx_a=0.6);
);
gfx_drawnumber(max_meterL,1);
gfx_drawstr("L");
gfx_r=gfx_g=gfx_b=1;
gfx_drawstr(" dB");

max_meterR>slider6-1 ? (gfx_r=gfx_b=0);
max_meterR>slider6 ? (gfx_r=1);
max_meterR>slider6+1 ? (gfx_g=0);
gfx_x+=5;
gfx_drawnumber(max_meterR,1);
gfx_drawstr("R");
gfx_r=gfx_g=gfx_b=1;

gfx_a=0.8; gfx_x+=8;
gfx_drawstr("|"); gfx_x+=8;
max_LR < 0 ? (gfx_drawstr("R-L:")) : (gfx_drawstr("L-R:"));
gfx_a=1;
mouse_x < 430 && mouse_x > 373 && mouse_y < 26 && mouse_y >0 ? (
  gfx_a=0.8;
  mouse_cap ? (max_LR=posLR=0; gfx_a=0.6);
);
abs(max_LR)>slider6-2 ? (gfx_r=gfx_b=0);
abs(max_LR)>slider6-1 ? (gfx_r=1);
abs(max_LR)>slider6 ? (gfx_g=0);
gfx_x+=5;
gfx_drawnumber(abs(max_LR),1);
gfx_drawstr("dB");
gfx_r=gfx_g=gfx_b=1;

g_meter > 0.049 ? g_meter*=1/5;
g_meter < max(g_meterL,g_meterR) && max(abs(spl0),abs(spl1)) > 0.01 ? g_meter = max(g_meterL,g_meterR);

gfx_x+=8; gfx_a=0.8;
gfx_drawstr("|"); gfx_x+=8;
gfx_drawnumber(g_meter,1);
gfx_drawstr("dB");

gfx_r=0; gfx_y+=gfx_texth+8;
gfx_x=109;
gfx_drawstr("Time position:");

gfx_x+=5;
#sposL=sprintf(#, "%.2d", posL/60);
#sposL+=":";
#sposL+=sprintf(#, "%.2d", posL%60);
gfx_drawstr(#sposL);

gfx_x+=2;
gfx_drawstr("|");
gfx_x+=3;
#sposR=sprintf(#, "%.2d", posR/60);
#sposR+=":";
#sposR+=sprintf(#, "%.2d", posR%60);
gfx_drawstr(#sposR);

gfx_x+=61;
#sposLR=sprintf(#, "%.2d", posLR/60);
#sposLR+=":";
#sposLR+=sprintf(#, "%.2d", posLR%60);
gfx_drawstr(#sposLR);
gfx_y+=gfx_texth+5;
gfx_h_=gfx_h-gfx_y;

// draw curve input gain/attenuation
gfx_a=gfx_r=1;
gfx_g=gfx_b=0.3;
gfx_x=j=0;
gfx_y=gfx_h;
while (j<max_x ? (
  val = arr_in[j];
  val = max(base_db,val);
  val = min(height_db+base_db,val);
  gr_hide=="attenuation" ? (
    val = (val-base_db)*gfx_h_/height_db;
    gfx_lineto(j*gfx_w/max_x, gfx_h-val);
  ) : (
    val *= gfx_h_/height_db;
    gfx_lineto(j*gfx_w/max_x, gfx_h-gfx_h_+val);
  );
  j+=1;
));

// draw curve out
gfx_a=gfx_r=gfx_g=gfx_b=1;
gfx_x=j=0;
gfx_y=gfx_h;
while (j<max_x ? (
  val = arr_out[j];
  val = max(base_db,val);
  val = min(height_db+base_db,val);
  val = (val-base_db)*gfx_h_/(height_db);
  gfx_lineto(j*gfx_w/max_x, gfx_h-val);
  j+=1;
));

// draw horz grid
gfx_y=gfx_h-gfx_h_; gfx_a=0.4;
cnt=20; j=0; grid_db=height_db+base_db;
while(j<=cnt ? (
  gfx_a=grid_db==0 ? 0.55:0.4;
  gfx_x=0;
  gfx_lineto(gfx_w,gfx_y);
  gfx_x=0;
  grid_db>0 ? #grid="+";
  grid_db==0 ? #grid=" ";
  grid_db<0 ? #grid="";
  #grid+=sprintf(#, "%d", grid_db);
  #grid+="dB";
  gfx_y+=2;
  gfx_drawstr(#grid);
  grid_db-=height_db/cnt;
  j+=1;
  gfx_y=gfx_h-gfx_h_+(j*(gfx_h_/cnt));
));



gfx_y=gfx_h-gfx_texth-5;
gfx_x=gr_hide=="input gain" ? gfx_w-85:gfx_w-93;
gfx_a=0.4;
mouse_x >= gfx_x && mouse_x <= gfx_w && mouse_y >= gfx_y && mouse_y <= gfx_h ? (
  gfx_a=0.6;
  mouse_cap && tg==0 ? (
    gr_hide=="attenuation" ? (
      gr_hide="input gain";
    ) : (
      gr_hide="attenuation";
    );
    memset(arr_in,0,max_x);
    tg=10; gfx_a=0.4;
  );
);
gfx_drawstr(gr_hide);
tg=tg ? tg-1:0;
